home *** CD-ROM | disk | FTP | other *** search
/ Acorn User: China / Acorn User China CD-ROM (UK) (Disc A) / Acorn User China CD-ROM (UK) (Disc A).bin / DEMON / RISCOS2 / ARCKA9Q1.ARC / c / FTPSERV < prev    next >
Encoding:
Text File  |  1992-06-26  |  24.8 KB  |  694 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define LINELEN         128     /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include <time.h>
  10. #include "global.h"
  11. #include "mbuf.h"
  12. #include "netuser.h"
  13. #include "timer.h"
  14. #include "tcp.h"
  15. #include "ftp.h"
  16. #include "misc.h"
  17. #include "arc.h"
  18.  
  19. static void ftpscs(struct tcb *, char, char);
  20. static void ftpscr(struct tcb *, int16);
  21. static void ftpcommand(struct ftp *);
  22. static int  pport(struct socket *, char *);
  23. static void ftplogin(struct ftp *, char *);
  24.  
  25.  
  26. /* Command table */
  27. static char *commands[] = {
  28.         "user",
  29. #define USER_CMD        0
  30.         "acct",
  31. #define ACCT_CMD        1
  32.         "pass",
  33. #define PASS_CMD        2
  34.         "type",
  35. #define TYPE_CMD        3
  36.         "list",
  37. #define LIST_CMD        4
  38.         "cwd",
  39. #define CWD_CMD         5
  40.         "dele",
  41. #define DELE_CMD        6
  42.         "name",
  43. #define NAME_CMD        7
  44.         "quit",
  45. #define QUIT_CMD        8
  46.         "retr",
  47. #define RETR_CMD        9
  48.         "stor",
  49. #define STOR_CMD        10
  50.         "port",
  51. #define PORT_CMD        11
  52.         "nlst",
  53. #define NLST_CMD        12
  54.         "pwd",
  55. #define PWD_CMD         13
  56.         "xpwd",                 /* For compatibility with 4.2BSD */
  57. #define XPWD_CMD        14
  58.         "mkd ",
  59. #define MKD_CMD         15
  60.         "xmkd",                 /* For compatibility with 4.2BSD */
  61. #define XMKD_CMD        16
  62.         "xrmd",                 /* For compatibility with 4.2BSD */
  63. #define XRMD_CMD        17
  64.         "rmd ",
  65. #define RMD_CMD         18
  66.         "stru",
  67. #define STRU_CMD        19
  68.         "mode",
  69. #define MODE_CMD        20
  70.         NULLCHAR
  71. };
  72.  
  73. /* Response messages */
  74. static char banner[] = "220 %s FTP server ready at %s\r\n";
  75. static char badcmd[] = "500 Unknown command\r\n";
  76. static char unsupp[] = "500 Unsupported command or option\r\n";
  77. static char givepass[] = "331 Enter PASS command\r\n";
  78. static char logged[] = "230 Logged in\r\n";
  79. static char loggeda[] = "230 Logged in as anonymous, restrictions apply\r\n";
  80. static char typeok[] = "200 Type OK\r\n";
  81. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  82. static char deleok[] = "250 File deleted\r\n";
  83. static char mkdok[] = "200 MKD ok\r\n";
  84. static char delefail[] = "550 Delete failed\r\n";
  85. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  86. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  87. static char badport[] = "501 Bad port syntax\r\n";
  88. static char unimp[] = "502 Command not yet implemented\r\n";
  89. static char bye[] = "221 Goodbye!\r\n";
  90. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  91. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  92. static char sending[] = "150 Opening data connection for %s %s\r\n";
  93. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  94. static char portok[] = "200 Port command okay\r\n";
  95. static char rxok[] = "226 File received OK\r\n";
  96. static char txok[] = "226 File sent OK\r\n";
  97. static char noperm[] = "550 Permission denied\r\n";
  98. static char noconn[] = "425 Data connection reset\r\n";
  99. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  100. static char okay[] = "200 Ok\r\n";
  101.  
  102. static struct tcb *ftp_tcb;
  103.  
  104. /* Start up FTP service */
  105. int ftp1(int argc, char **argv)
  106. {
  107.         struct socket lsocket;
  108.  
  109.         lsocket.address = ip_addr;
  110.         if(argc < 2)
  111.                 lsocket.port = FTP_PORT;
  112.         else
  113.                 lsocket.port = atoi(argv[1]);
  114.  
  115.         ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,(void(*)())ftpscr,NULLVFP,(void(*)())ftpscs,0,(char *)NULL);
  116.         return(0);
  117. }
  118. /* Shut down FTP server */
  119. int ftp0(void)
  120. {
  121.         if(ftp_tcb != NULLTCB)
  122.                 close_tcp(ftp_tcb);
  123.         return(0);
  124. }
  125. /* FTP Server Control channel State change upcall handler */
  126. static void ftpscs(struct tcb *tcb, char old, char new)
  127. {
  128.         extern char hostname[];
  129.         struct ftp *ftp;
  130.         time_t t;
  131.         char *cp,*cp1;
  132.  
  133.         old = old;
  134.  
  135.         switch(new){
  136. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  137.  * leaving it unset waits for the three-way handshake to complete before
  138.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  139.  * so its use is not (yet) recommended.
  140. */
  141. #ifdef  QUICKSTART
  142.         case SYN_RECEIVED:
  143. #else
  144.         case ESTABLISHED:
  145. #endif
  146.                 if((ftp = ftp_create(LINELEN)) == NULLFTP){
  147.                         /* No space, kill connection */
  148.                         close_tcp(tcb);
  149.                         return;
  150.                 }
  151.                 ftp->control = tcb;             /* Downward link */
  152.                 tcb->user = (char *)ftp;        /* Upward link */
  153.  
  154.                 /* Set default data port */
  155.                 ftp->port.address = tcb->conn.remote.address;
  156.                 ftp->port.port = FTPD_PORT;
  157.  
  158.                 /* Note current directory */
  159.                 log_event(tcb,"open FTP");
  160.                 time(&t);
  161.                 cp = ctime(&t);
  162.                 if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  163.                         *cp1 = '\0';
  164.                 tprintf(ftp->control,banner,hostname,cp);
  165.                 break;          
  166.         case CLOSE_WAIT:
  167.                 close_tcp(tcb);
  168.                 break;
  169.         case CLOSED:
  170.                 log_event(tcb,"close FTP");
  171.                 if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  172.                         ftp_delete(ftp);
  173.                 /* Check if server is being shut down */
  174.                 if(tcb == ftp_tcb)
  175.                         ftp_tcb = NULLTCB;
  176.                 del_tcp(tcb);
  177.                 break;
  178.         }
  179. }
  180.  
  181. /* FTP Server Control channel Receiver upcall handler */
  182. static void ftpscr(struct tcb *tcb, int16 cnt)
  183. {
  184.         register struct ftp *ftp;
  185.         char c;
  186.         struct mbuf *bp;
  187.  
  188.         cnt = cnt;
  189.  
  190.         if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  191.                 /* Unknown connection, just kill it */
  192.                 close_tcp(tcb);
  193.                 return;
  194.         }
  195.         switch(ftp->state){
  196.         case COMMAND_STATE:
  197.                 /* Assemble an input line in the session buffer. Return if incomplete */
  198.                 recv_tcp(tcb,&bp,0);
  199.                 while(pullup(&bp,&c,1) == 1){
  200.                         switch(c){
  201.                         case '\r':      /* Strip cr's */
  202.                                 continue;
  203.                         case '\n':      /* Complete line; process it */
  204.                                 ftp->buf[ftp->cnt] = '\0';
  205.                                 ftpcommand(ftp);
  206.                                 ftp->cnt = 0;
  207.                                 break;
  208.                         default:        /* Assemble line */
  209.                                 if(ftp->cnt != LINELEN-1)
  210.                                         ftp->buf[ftp->cnt++] = c;
  211.                                 break;
  212.                         }
  213.                 }
  214.                 /* else no linefeed present yet to terminate command */
  215.                 break;
  216.         case SENDING_FILE_STATE:
  217.         case SENDING_DATA_STATE:
  218.         case RECEIVING_STATE:
  219.                 /* Leave commands pending on receive queue until
  220.                  * present command is done
  221.                  */
  222.                 break;
  223.         }
  224. }
  225.  
  226. /* FTP server data channel connection state change upcall handler */
  227. void ftpsds(struct tcb *tcb, char old, char new)
  228. {
  229.         register struct ftp *ftp;
  230.  
  231.         if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  232.                 /* Unknown connection. Kill it */
  233.                 del_tcp(tcb);
  234.         } else if((old == FINWAIT1 || old == CLOSING) && (ftp->state == SENDING_FILE_STATE || ftp->state == SENDING_DATA_STATE)){
  235.                 /* We've received an ack of our FIN while sending; we're done */
  236.                 ftp->state = COMMAND_STATE;
  237.                 tprintf(ftp->control,txok);
  238.                 /* Kick command parser if something is waiting */
  239.                 if(ftp->control->rcvcnt != 0)
  240.                         ftpscr(ftp->control,ftp->control->rcvcnt);
  241.         } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  242.                 /* FIN received on incoming file */
  243.                 close_tcp(tcb);
  244.                 if(ftp->fp != stdout)
  245.                         fclose(ftp->fp);
  246.                 ftp->fp = NULLFILE;
  247.                 ftp->state = COMMAND_STATE;
  248.                 tprintf(ftp->control,rxok);
  249.                 /* Kick command parser if something is waiting */
  250.                 if(ftp->control->rcvcnt != 0)
  251.                         ftpscr(ftp->control,ftp->control->rcvcnt);
  252.         } else if(new == CLOSED){
  253.                 if(tcb->reason != NORMAL){
  254.                         /* Data connection was reset, complain about it */
  255.                         tprintf(ftp->control,noconn);
  256.                         /* And clean up */
  257.                         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  258.                                 fclose(ftp->fp);
  259.                         ftp->fp = NULLFILE;
  260.                         ftp->state = COMMAND_STATE;
  261.                         /* Kick command parser if something is waiting */
  262.                         if(ftp->control->rcvcnt != 0)
  263.                                 ftpscr(ftp->control,ftp->control->rcvcnt);
  264.                 }
  265.                 /* Clear only if another transfer hasn't already started */
  266.                 if(ftp->data == tcb)
  267.                         ftp->data = NULLTCB;
  268.                 del_tcp(tcb);
  269.         }
  270. }
  271.  
  272. /* Parse and execute ftp commands */
  273. static void ftpcommand(register struct ftp *ftp)
  274. {
  275.         char *cmd,*arg,*cp,**cmdp,*file;
  276.         char *mode;
  277.         struct socket dport;
  278.         int i;
  279.  
  280.         cmd = ftp->buf;
  281.         if(ftp->cnt == 0){
  282.                 /* Can't be a legal FTP command */
  283.                 tprintf(ftp->control,badcmd);
  284.                 return;
  285.         }       
  286.         cmd = ftp->buf;
  287.  
  288.         /* Translate entire buffer to lower case */
  289.         for(cp = cmd;*cp != '\0';cp++)
  290.                 *cp = tolower(*cp);
  291.  
  292.         /* Find command in table; if not present, return syntax error */
  293.         for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  294.                 if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  295.                         break;
  296.         if(*cmdp == NULLCHAR){
  297.                 tprintf(ftp->control,badcmd);
  298.                 return;
  299.         }
  300.         /* Allow only USER, PASS and QUIT before logging in */
  301.         if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){
  302.                 switch(cmdp-commands){
  303.                 case USER_CMD:
  304.                 case PASS_CMD:
  305.                 case QUIT_CMD:
  306.                         break;
  307.                 default:
  308.                         tprintf(ftp->control,notlog);
  309.                         return;
  310.                 }
  311.         }
  312.         arg = &cmd[strlen(*cmdp)];
  313.         while(*arg == ' ')
  314.                 arg++;
  315.  
  316.         /* Execute specific command */
  317.         switch(cmdp-commands){
  318.         case USER_CMD:
  319.                 if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  320.                         close_tcp(ftp->control);
  321.                         break;
  322.                 }
  323.                 strcpy(ftp->username,arg);
  324.                 tprintf(ftp->control,givepass);
  325.                 /* erase all user info from possible previous session */
  326.                 for(i = 0; i < MAXPATH; i++){
  327.                         if(ftp->path[i] != NULLCHAR){
  328.                                 free(ftp->path[i]);
  329.                                 ftp->path[i] = NULLCHAR;
  330.                         }
  331.                         ftp->perms[i] = 0;
  332.                 }
  333.                 if(ftp->cd != NULLCHAR){
  334.                         free(ftp->cd);
  335.                         ftp->cd = NULLCHAR;
  336.                 }
  337.                 break;
  338.         case TYPE_CMD:
  339.                 switch(arg[0]){
  340.                 case 'A':
  341.                 case 'a':       /* Ascii */
  342.                         ftp->type = ASCII_TYPE;
  343.                         tprintf(ftp->control,typeok);
  344.                         break;
  345.                 case 'l':
  346.                 case 'L':
  347.                         while(*arg != ' ' && *arg != '\0')
  348.                                 arg++;
  349.                         if(*arg == '\0' || *++arg != '8'){
  350.                                 tprintf(ftp->control,only8);
  351.                                 break;
  352.                         }       /* Note fall-thru */
  353.                 case 'B':
  354.                 case 'b':       /* Binary */
  355.                 case 'I':
  356.                 case 'i':       /* Image */
  357.                         ftp->type = IMAGE_TYPE;
  358.                         tprintf(ftp->control,typeok);
  359.                         break;
  360.                 default:        /* Invalid */
  361.                         tprintf(ftp->control,badtype,arg);
  362.                         break;
  363.                 }
  364.                 break;
  365.         case QUIT_CMD:
  366.                 tprintf(ftp->control,bye);
  367.                 close_tcp(ftp->control);
  368.                 break;
  369.         case RETR_CMD:
  370.                 /* Disk operation; return ACK now */
  371.                 tcp_output(ftp->control);
  372.                 file = pathname(ftp->cd,arg);
  373.                 if(ftp->type == IMAGE_TYPE)
  374.                         mode = binmode[READ_BINARY];
  375.                 else
  376.                         mode = "r";
  377.                 if(!permcheck(ftp,RETR_CMD,file)){
  378.                         tprintf(ftp->control,noperm);
  379.                 } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  380.                         tprintf(ftp->control,cantopen,file);
  381.                 } else {
  382.                         log_event(ftp->control,"RETR %s",file);
  383.                         dport.address = ip_addr;
  384.                         dport.port = FTPD_PORT;
  385.                         ftp->state = SENDING_FILE_STATE;
  386.                         tprintf(ftp->control,sending,"RETR",arg);
  387.                         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  388.                          0,NULLVFP,(void(*)())ftpdt,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  389.                 }
  390.                 free(file);
  391.                 break;
  392.         case STOR_CMD:
  393.                 /* Disk operation; return ACK now */
  394.                 tcp_output(ftp->control);
  395.                 file = pathname(ftp->cd,arg);
  396.                 if(ftp->type == IMAGE_TYPE)
  397.                         mode = binmode[WRITE_BINARY];
  398.                 else
  399.                         mode = "w";
  400.                 if(!permcheck(ftp,STOR_CMD,file)){
  401.                         tprintf(ftp->control,noperm);
  402.                         free(file);
  403.                         break;
  404.                 } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  405.                         tprintf(ftp->control,cantmake,file);
  406.                 } else {
  407.                         log_event(ftp->control,"STOR %s",file);
  408.                         dport.address = ip_addr;
  409.                         dport.port = FTPD_PORT;
  410.                         ftp->state = RECEIVING_STATE;
  411.                         tprintf(ftp->control,sending,"STOR",arg);
  412.                         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  413.                          0,(void(*)())ftpdr,NULLVFP,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  414.                 }
  415.                 free(file);
  416.                 break;
  417.         case PORT_CMD:
  418.                 if(pport(&ftp->port,arg) == -1){
  419.                         tprintf(ftp->control,badport);
  420.                 } else {
  421.                         tprintf(ftp->control,portok);
  422.                 }
  423.                 break;
  424.         case LIST_CMD:
  425.                 /* Disk operation; return ACK now */
  426.                 tcp_output(ftp->control);
  427.                 file = pathname(ftp->cd,arg);
  428.                 if(!permcheck(ftp,RETR_CMD,file)){
  429.                         tprintf(ftp->control,noperm);
  430.                 } else if((ftp->p = dir(file,1)) == NULLCHAR){
  431.                         tprintf(ftp->control,nodir,file);
  432.                 } else {
  433.                         dport.address = ip_addr;
  434.                         dport.port = FTPD_PORT;
  435.                         ftp->state = SENDING_DATA_STATE;
  436.                         ftp->cp    = ftp->p;
  437.                         tprintf(ftp->control,sending,"LIST",file);
  438.                         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  439.                          0,NULLVFP,(void(*)())ftpdt,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  440.                 }
  441.                 free(file);
  442.                 break;
  443.         case NLST_CMD:
  444.                 /* Disk operation; return ACK now */
  445.                 tcp_output(ftp->control);
  446.                 file = pathname(ftp->cd,arg);
  447.                 if(!permcheck(ftp,RETR_CMD,file)){
  448.                         tprintf(ftp->control,noperm);
  449.                 } else if((ftp->p = dir(file,0)) == NULLCHAR){
  450.                         tprintf(ftp->control,nodir,file);
  451.                 } else {
  452.                         dport.address = ip_addr;
  453.                         dport.port = FTPD_PORT;
  454.                         ftp->state = SENDING_DATA_STATE;
  455.                         ftp->cp    = ftp->p;
  456.                         tprintf(ftp->control,sending,"NLST",file);
  457.                         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  458.                          0,NULLVFP,(void(*)())ftpdt,(void(*)())ftpsds,ftp->control->tos,(char *)ftp);
  459.                 }
  460.                 free(file);
  461.                 break;
  462.         case CWD_CMD:
  463.                 tcp_output(ftp->control);       /* Disk operation; return ACK now */
  464.  
  465.                 file = pathname(ftp->cd,arg);
  466.                 if(!permcheck(ftp,RETR_CMD,file)){
  467.                         tprintf(ftp->control,noperm);
  468.                         free(file);
  469.                 } else if(access(file,0) == 0){ /* See if it exists */
  470.                         /* Succeeded, record in control block */
  471.                         free(ftp->cd);
  472.                         ftp->cd = file;
  473.                         tprintf(ftp->control,pwdmsg,file);
  474.                 } else {
  475.                         /* Failed, don't change anything */
  476.                         tprintf(ftp->control,nodir,file);
  477.                         free(file);
  478.                 }
  479.                 break;
  480.         case XPWD_CMD:
  481.         case PWD_CMD:
  482.                 tprintf(ftp->control,pwdmsg,ftp->cd);
  483.                 break;
  484.         case ACCT_CMD:          
  485.                 tprintf(ftp->control,unimp);
  486.                 break;
  487.         case DELE_CMD:
  488.                 file = pathname(ftp->cd,arg);
  489.                 if(!permcheck(ftp,DELE_CMD,file)){
  490.                         tprintf(ftp->control,noperm);
  491.                 } else if(remove(file) == 0){
  492.                         tprintf(ftp->control,deleok);
  493.                 } else {
  494.                         tprintf(ftp->control,delefail);
  495.                 }
  496.                 free(file);
  497.                 break;
  498.         case PASS_CMD:
  499.                 tcp_output(ftp->control);       /* Send the ack now */
  500.                 ftplogin(ftp,arg);                      
  501.                 break;
  502.         case XMKD_CMD:
  503.         case MKD_CMD:
  504.                 file = pathname(ftp->cd,arg);
  505.                 if(!permcheck(ftp,MKD_CMD,file)){
  506.                         tprintf(ftp->control,noperm);
  507.                 } else if(mkdir(file) == 0){
  508.                         tprintf(ftp->control,mkdok);
  509.                 } else {
  510.                         tprintf(ftp->control,cantmake);
  511.                 }
  512.                 free(file);
  513.                 break;
  514.         case XRMD_CMD:
  515.         case RMD_CMD:
  516.                 file = pathname(ftp->cd,arg);
  517.                 if(!permcheck(ftp,RMD_CMD,file)){
  518.                         tprintf(ftp->control,noperm);
  519.                 } else if(rmdir(file) == 0){
  520.                         tprintf(ftp->control,deleok);
  521.                 } else {
  522.                         tprintf(ftp->control,delefail);
  523.                 }
  524.                 free(file);
  525.                 break;
  526.         case STRU_CMD:
  527.                 if(tolower(arg[0]) != 'f')
  528.                         tprintf(ftp->control,unsupp);
  529.                 else
  530.                         tprintf(ftp->control,okay);
  531.                 break;
  532.         case MODE_CMD:
  533.                 if(tolower(arg[0]) != 's')
  534.                         tprintf(ftp->control,unsupp);
  535.                 else
  536.                         tprintf(ftp->control,okay);
  537.                 break;
  538.         }
  539. }
  540. static int pport(struct socket *sock, char *arg)
  541. {
  542.         int32 n;
  543.         int i;
  544.  
  545.         n = 0;
  546.         for(i=0;i<4;i++){
  547.                 n = atoi(arg) + (n << 8);
  548.                 if((arg = strchr(arg,',')) == NULLCHAR)
  549.                         return -1;
  550.                 arg++;
  551.         }
  552.         sock->address = n;
  553.         n = atoi(arg);
  554.         if((arg = strchr(arg,',')) == NULLCHAR)
  555.                 return -1;
  556.         arg++;
  557.         n = atoi(arg) + (n << 8);
  558.         sock->port = (int16)n;
  559.         return 0;
  560. }
  561. /* Attempt to log in the user whose name is in ftp->username and password
  562.  * in pass
  563.  */
  564. static void ftplogin(struct ftp *ftp, char *pass)
  565. {
  566.         char buf[80];
  567.         FILE *fp;
  568.         char *user;
  569.         char *password;
  570.         char *root;
  571.         char *permissions;
  572.         int anony = 0;
  573.         int i;
  574.  
  575.         if ((fp = fopen(userfile,"r")) == NULLFILE){
  576.                 /* Userfile doesn't exist */
  577.                 tprintf(ftp->control, noperm);
  578.                 return;
  579.         }
  580.         while (fgets(buf, sizeof(buf), fp) != NULLCHAR){
  581.                 if (buf[0] == '#')
  582.                         continue;       /* Comment */
  583.                 if ((user = strtok(buf," \n\t")) == NULLCHAR)
  584.                         continue;
  585.                 if (strcmp(ftp->username, user) == 0)
  586.                         break;          /* Found user name */
  587.         }
  588.         if (feof(fp)){
  589.                 /* User name not found in file or is incomplete */
  590.                 fclose(fp);
  591.                 tprintf(ftp->control,noperm);
  592.                 return;
  593.         }
  594.         fclose(fp);
  595.  
  596.         if ((password = strtok(NULLCHAR," \n\t")) == NULLCHAR){
  597.                 /* Password required, non given */
  598.                 tprintf(ftp->control, noperm);
  599.                 return;
  600.         }
  601.  
  602.         if (strcmp(password, "*") == 0)
  603.                 anony = 1;      /* User ID is password-free */
  604.  
  605.         if (!anony && strcmp(password, pass) != 0){
  606.                 /* Password required, but wrong one given */
  607.                 tprintf(ftp->control, noperm);
  608.                 return;
  609.         }
  610.  
  611.         root = strtok(NULLCHAR, " \n\t");
  612.  
  613.         for(i = 0; i < MAXPATH; i++){
  614.                 if((permissions = strtok(NULLCHAR, " \n\t")) == NULLCHAR){
  615.                         /* Permission field missing, assume end of line */
  616.                         break;
  617.                 }
  618.                 ftp->path[i] = malloc((unsigned)strlen(root) + 1);
  619.                 strcpy(ftp->path[i], root);
  620.                 ftp->perms[i] = atoi(permissions);
  621.                 if((root = strtok(NULLCHAR, " \n\t")) == NULLCHAR){
  622.                         /* No next path field, so assume end of line */
  623.                         break;
  624.                 }
  625.         }
  626.  
  627.         /* Set up current directory and LAST specified path prefix */
  628.         for (i = MAXPATH - 1; i >= 0; i--)
  629.                 if (ftp->perms[i])
  630.                         break;
  631.  
  632.         ftp->cd = malloc((unsigned)strlen(ftp->path[i]) + 1);
  633.         strcpy(ftp->cd, ftp->path[i]);
  634.  
  635.         if (!anony) {
  636.                 tprintf(ftp->control, logged);
  637.                 log_event(ftp->control, "%s logged in", ftp->username);
  638.         } else {
  639.                 tprintf(ftp->control, loggeda);
  640.                 log_event(ftp->control, "%s logged in, ID %s", ftp->username, pass);
  641.         }
  642. }               
  643.  
  644. /* Illegal characters in a RISC OS filename */
  645. char badchars[] = "\"[]:|<>+=;,";
  646.  
  647. /* Return 1 if the file operation is allowed, 0 otherwise */
  648. int permcheck(struct ftp *ftp, int op, char *file)
  649. {
  650.         char *cp;
  651.         int i;
  652.  
  653.         if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  654.                 return 0;       /* Probably hasn't logged in yet */
  655.         /* Check for characters illegal in RISC OS file names */
  656.         for(cp = badchars;*cp != '\0';cp++){
  657.                 if(strchr(file,*cp) != NULLCHAR)
  658.                         return 0;       
  659.         }
  660.         /* The target file must be under the users allowed path */
  661.         for(i = 0; i < MAXPATH; i++)
  662.                 if(ftp->path[i] != NULLCHAR &&
  663.                    strncmp(file, ftp->path[i], strlen(ftp->path[i])) == 0)
  664.                         break;
  665.  
  666.         if (i == MAXPATH)
  667.                 return 0;
  668.  
  669.         switch(op){
  670.         case RETR_CMD:
  671.                 /* User must have permission to read files */
  672.                 if(ftp->perms[i] & FTP_READ)
  673.                         return 1;
  674.                 return 0;
  675.         case DELE_CMD:
  676.         case RMD_CMD:
  677.                 /* User must have permission to (over)write files */
  678.                 if(ftp->perms[i] & FTP_WRITE)
  679.                         return 1;
  680.                 return 0;
  681.         case STOR_CMD:
  682.         case MKD_CMD:
  683.                 /* User must have permission to (over)write files, or permission
  684.                  * to create them if the file doesn't already exist
  685.                  */
  686.                 if(ftp->perms[i] & FTP_WRITE)
  687.                         return 1;
  688.                 if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  689.                         return 1;
  690.                 return 0;
  691.         }
  692.         return 0;       /* "can't happen" -- keep lint happy */
  693. }
  694.